#include <iostream>
#include "abc_context.h"
#include "method_filter.h"
#include "method_inspect.h"
#include "abckit_apis.h"
#include <vector>
#include <cstring>

auto mi_impl = MethodInspectApiImpl();

void showMethods(MethodFilter finder)
{
    auto view = finder.filter([](const std::shared_ptr<AbcFunc> &func)
                              { return true; })
                    .getCopies();

    // 使用视图
    auto mi_impl = MethodInspectApiImpl();
    std::cout << "Found " << finder.count() << " matching methods:\n";
    for (const auto &func : view)
    {
        std::cout << "module: " << mi_impl->getModulePath(func);
        std::cout << ", package: " << mi_impl->getPackagePath(func);
        std::cout << ", class: " << mi_impl->getClassName(func);
        std::cout << ", method: " << mi_impl->getMethodName(func) << std::endl;
    }
}

std::shared_ptr<AbcFunc> getMethod(MethodFilter finder, std::string className, std::string methodName)
{

    auto view = finder.filter([className, methodName](const std::shared_ptr<AbcFunc> &func)
                              {
                                if(mi_impl->getClassName(func) == className && mi_impl ->getMethodName(func) == methodName){
                                    return true;
                                }
                                return false; })
                    .getCopies();
    return view.size() > 0 ? view[0] : std::shared_ptr<AbcFunc>();
}

AbckitBasicBlock *loadMethodStartSuccBlock(AbckitGraph *graph)
{
    auto startBB = g_implG->gGetStartBasicBlock(graph);
    std::vector<AbckitBasicBlock *> succBBs;
    g_implG->bbVisitSuccBlocks(startBB, &succBBs, [](AbckitBasicBlock *succBasicBlock, void *data)
                               {
        auto *succs = reinterpret_cast<std::vector<AbckitBasicBlock *> *>(data);
        succs->emplace_back(succBasicBlock);
        return true; });

    return succBBs[0];
}

struct ImportContext
{
    std::string importDescriptorName;
    AbckitCoreImportDescriptor *targetImportDesc;
};

AbckitCoreImportDescriptor *getImportDescriptor(AbckitCoreModule *module, std::string importDescriptorName)
{
    ImportContext context;
    context.importDescriptorName = importDescriptorName;
    g_implI->moduleEnumerateImports(module, &context, [](AbckitCoreImportDescriptor *i, void *data)
                                    {
        auto *context = reinterpret_cast<ImportContext *>(data);
        std::string name = g_implI -> abckitStringToString(g_implI -> importDescriptorGetAlias(i));
        if(name == context->importDescriptorName){
            context->targetImportDesc = i;
        }
        return true; });
    return context.targetImportDesc;
}

void BeforeExecution(const AbcContext *abccontext, std::shared_ptr<AbcFunc> &abcFunc)
{
    std::cout << "module: " << mi_impl->getModulePath(abcFunc);
    std::cout << ", package: " << mi_impl->getPackagePath(abcFunc);
    std::cout << ", class: " << mi_impl->getClassName(abcFunc);
    std::cout << ", method: " << mi_impl->getMethodName(abcFunc) << std::endl;

    AbckitGraph *graph = g_implI->createGraphFromFunction(abcFunc.get()->func);
    auto *succBlock = loadMethodStartSuccBlock(graph);
    auto importDesc = getImportDescriptor(g_implI->functionGetModule(abcFunc.get()->func), "MyAspect");
    std::string name = g_implI->abckitStringToString(g_implI->importDescriptorGetAlias(importDesc));
    auto ldexternalmodulevar = g_dynG->iCreateLdexternalmodulevar(graph, importDesc);
    g_implG->bbAddInstFront(succBlock, ldexternalmodulevar);
    auto *myAspect = g_dynG->iCreateThrowUndefinedifholewithname(graph, ldexternalmodulevar, g_implM->createString(abccontext->file, "MyAspect", strlen("MyAspect")));
    g_implG->iInsertAfter(myAspect, ldexternalmodulevar);

    auto *addBefore = g_dynG->iCreateLdobjbyname(graph, myAspect, g_implM->createString(abccontext->file, "beforeExecution", strlen("beforeExecution")));
    g_implG->iInsertAfter(addBefore, myAspect);

    auto *call = g_dynG->iCreateCallthis0(graph, addBefore, myAspect);
    g_implG->iInsertAfter(call, addBefore);

    auto method = abcFunc.get()->func;
    g_implM->functionSetGraph(method, graph);
    g_impl->destroyGraph(graph);
}

void test()
{
    char *abc_path = "/app/codes/aspect-framework/src/modules.abc";
    auto abccontext = AbcContextApiImpl(abc_path);
    auto methods = abccontext->methods;
    MethodFilter finder(methods);
    // showMethods(finder);
    auto showRstFunc = getMethod(finder, "Index", "showRst");
    auto getLocationFunc = getMethod(finder, "Index", "getLocation");
    auto initialRenderFunc = getMethod(finder, "Index", "initialRender");

    // BeforeExecution(abccontext, showRstFunc);

    char *outpath = "/app/codes/aspect-framework/src/modules_before_execution.abc";
    g_impl->writeAbc(abccontext->file, outpath, strlen(outpath));
}

int main()
{
    test();
    std::cout << 123 << std::endl;
}